// @Creation:	29.04.2012
// @Version:	30.04.2012
// 
// @cpu:	x86,
// @author:	alexey kurshakov [alexey.kurshakov@gmail.com]
//
// @Contents:	trace.go 
//	Набор функций для ведения трассировочных сообщений
//

package trace

/*
#include <string.h>
*/
import "C"

import (
	"fmt"
	"time"
	"unsafe"
)

// Служебные зоны трассировки, список которых может быть расширен
const (
	ZoneInfo            int = 0
	ZoneWarn            int = 1
	ZoneError           int = 2
	ZoneMax             int = 16
	defaultEnablesZones int = (1 << uint(ZoneInfo)) +
		(1 << uint(ZoneWarn)) + (1 << uint(ZoneError))
)

// Переменные для конфигурации пакета trace
var (
	traceToFile bool = false
	traceOff    bool = false
	debugTrace  bool = false
)

var (
	traceStart  bool     = false
	zoneEnables int      = defaultEnablesZones
	zoneNames   []string = []string{"I", "W", "E", "", "", "", "", "", "",
		"", "", "", "", "", "", ""}
)

// Разрешить/запретить вывод трассировочных зон в указанную зону
func SetZone(z int, f bool) {
	if f == true {
		zoneEnables |= 1 << uint(z)
	} else {
		zoneEnables = 0
	}
}

// Возвращает true, если разрешен вывод в указанную зону
func IsZoneEnabled(z int) bool {
	return (zoneEnables & 1 << uint(z)) > 0
}

// Установить новое имя для указанной зоны
func SetZoneName(z int, name string) {
	if z >= ZoneMax {
		return
	}
	zoneNames[z] = name
}

// Вывод памяти в 16-виде на экран
func TraceHexPtr(ptr unsafe.Pointer, size int) {
	if traceOff == true || traceStart == false {
		return
	}

	var b []byte = make([]byte, size)
	C.memcpy(unsafe.Pointer(&(b[0])), ptr, C.size_t(size))
	TraceHex(b)
}

// Вывод памяти в 16-виде на экран
func TraceHex(memory []byte) {
	if traceOff == true || traceStart == false {
		return
	}
	i := 0
	tempStr := ""
	for size := len(memory); size > 0; size-- {
		tempStr += fmt.Sprintf("%02X ", memory[i])
		i++
		if (i % 16) == 0 {
			fmt.Println(tempStr)
			if traceToFile == true {
				traceFileWrite(tempStr)
			}
			tempStr = ""
		}
	}
	if tempStr != "" {
		fmt.Println(tempStr)
		if traceToFile == true {
			traceFileWrite(tempStr)
		}
	}
}

// Вывод памяти в текстовом виде на экран
func TraceTextPtr(ptr unsafe.Pointer, size int) {
	if traceOff == true || traceStart == false {
		return
	}
	var b []byte = make([]byte, size)
	C.memcpy(unsafe.Pointer(&(b[0])), ptr, C.size_t(size))
	TraceText(b)
}

// Вывод памяти в текстовом виде на экран
func TraceText(memory []byte) {
	if traceOff == true || traceStart == false {
		return
	}
	i := 0
	tempStr := ""
	for size := len(memory); size > 0; size-- {
		tempStr += fmt.Sprintf("%c", memory[i])
		i++
		if (i % 32) == 0 {
			fmt.Println(tempStr)
			if traceToFile == true {
				traceFileWrite(tempStr)
			}
			tempStr = ""
		}
	}
	if tempStr != "" {
		fmt.Println(tempStr)
		if traceToFile == true {
			traceFileWrite(tempStr)
		}
	}
}

// Открыть трассировщик
func Start() {
	traceStart = true
	if traceToFile == true {
		traceFileOpen()
	}
}

// Закрыть трассировщик
func Stop() {
	traceStart = false
	if traceToFile == true {
		traceFileClose()
		time.Sleep(time.Millisecond * 100)
	}
}

// Вывод отладочных трассировочных сообщений
func DebugMsg(z int, format string, a ...interface{}) {
	if debugTrace == false ||
		traceOff == true || traceStart == false {
		return
	}
	if IsZoneEnabled(z) == false {
		return
	}
	z = 1 << uint(z)
	i := 0
	for cmp := 1; cmp < (1 << uint(ZoneMax)); cmp <<= 1 {
		if (z & cmp) > 0 {
			break
		}
		i++
	}
	format = zoneNames[i] + ":\t" + format
	switch len(a) {
	case 1:
		format = fmt.Sprintf(format, a[0])
	case 2:
		format = fmt.Sprintf(format, a[0], a[1])
	case 3:
		format = fmt.Sprintf(format, a[0], a[1], a[2])
	case 4:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3])
	case 5:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4])
	case 6:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5])
	case 7:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6])
	case 8:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])
	case 9:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8])
	case 10:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9])
	case 11:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10])
	case 12:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11])
	case 13:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12])
	case 14:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12], a[13])
	case 15:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
	case 16:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15])
	}
	if traceToFile == true {
		traceFileWrite(format)
	}
	fmt.Println(format)
}

// Вывод трассировочных сообщений, если трассировка разрешена
func RetailMsg(z int, format string, a ...interface{}) {
	if traceOff == true || traceStart == false {
		return
	}
	if IsZoneEnabled(z) == false {
		return
	}
	z = 1 << uint(z)
	i := 0
	for cmp := 1; cmp < (1 << uint(ZoneMax)); cmp <<= 1 {
		if (z & cmp) > 0 {
			break
		}
		i++
	}
	format = zoneNames[i] + ":\t" + format
	switch len(a) {
	case 1:
		format = fmt.Sprintf(format, a[0])
	case 2:
		format = fmt.Sprintf(format, a[0], a[1])
	case 3:
		format = fmt.Sprintf(format, a[0], a[1], a[2])
	case 4:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3])
	case 5:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4])
	case 6:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5])
	case 7:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6])
	case 8:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])
	case 9:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8])
	case 10:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9])
	case 11:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10])
	case 12:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11])
	case 13:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12])
	case 14:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12], a[13])
	case 15:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
	case 16:
		format = fmt.Sprintf(format, a[0], a[1], a[2], a[3], a[4], a[5], a[6],
			a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15])
	}
	if traceToFile == true {
		traceFileWrite(format)
	}
	fmt.Println(format)
}
